use crate::errors::AppError;
use crate::models::tutor::{NewTutor, Tutor, UpdateTutor};
use sqlx::postgres::PgPool;

pub async fn get_all_tutors_db(pool: &PgPool) -> Result<Vec<Tutor>, AppError> {
    let tutor_rows =
        sqlx::query!("SELECT tutor_id, tutor_name, tutor_pic_url, tutor_profile FROM ezy_tutor")
            .fetch_all(pool)
            .await?;
    let tutors: Vec<Tutor> = tutor_rows
        .iter()
        .map(|tutor_row| Tutor {
            tutor_id: tutor_row.tutor_id,
            tutor_name: tutor_row.tutor_name.clone(),
            tutor_pic_url: tutor_row.tutor_pic_url.clone(),
            tutor_profile: tutor_row.tutor_profile.clone(),
        })
        .collect();

    match tutors.len() {
        0 => Err(AppError::NotFound("No tutors found".into())),
        _ => Ok(tutors),
    }
}

pub async fn get_tutor_details_db(pool: &PgPool, tutor_id: i32) -> Result<Tutor, AppError> {
    let tutor_row = sqlx::query!("SELECT tutor_id, tutor_name, tutor_pic_url, tutor_profile FROM ezy_tutor WHERE tutor_id = $1", tutor_id).fetch_one(pool).await.map(|tutor_row| Tutor {
    tutor_id: tutor_row.tutor_id,
    tutor_name: tutor_row.tutor_name.clone(),
    tutor_pic_url: tutor_row.tutor_pic_url.clone(),
    tutor_profile: tutor_row.tutor_profile.clone(),
  }).map_err(|_| AppError::NotFound("Tutor not found".into()))?;

    Ok(tutor_row)
}

pub async fn update_tutor_details_db(
    pool: &PgPool,
    tutor_id: i32,
    change_tutor: UpdateTutor,
) -> Result<Tutor, AppError> {
    let tutor_row = sqlx::query!("SELECT tutor_id, tutor_name, tutor_pic_url, tutor_profile FROM ezy_tutor WHERE tutor_id = $1", tutor_id).fetch_one(pool).await.map_err(|_| AppError::NotFound("Tutor not found".into()))?;

    let new_tutor_record = Tutor {
        tutor_id: tutor_row.tutor_id,
        tutor_name: if let Some(name) = change_tutor.tutor_name {
            name
        } else {
            tutor_row.tutor_name
        },
        tutor_pic_url: if let Some(pic_url) = change_tutor.tutor_pic_url {
            pic_url
        } else {
            tutor_row.tutor_pic_url
        },
        tutor_profile: if let Some(profile) = change_tutor.tutor_profile {
            profile
        } else {
            tutor_row.tutor_profile
        },
    };

    let tutor_update_row = sqlx::query!("UPDATE ezy_tutor SET tutor_name = $1, tutor_pic_url = $2, tutor_profile = $3 WHERE tutor_id = $4 RETURNING tutor_id, tutor_name, tutor_pic_url, tutor_profile", new_tutor_record.tutor_name, new_tutor_record.tutor_pic_url, new_tutor_record.tutor_profile, tutor_id).fetch_one(pool).await?;

    Ok(Tutor {
        tutor_id: tutor_update_row.tutor_id,
        tutor_name: tutor_update_row.tutor_name,
        tutor_pic_url: tutor_update_row.tutor_pic_url,
        tutor_profile: tutor_update_row.tutor_profile,
    })
}

pub async fn post_new_tutor_db(pool: &PgPool, new_tutor: NewTutor) -> Result<Tutor, AppError> {
    let tutor_row = sqlx::query!("INSERT INTO ezy_tutor (tutor_name, tutor_pic_url, tutor_profile) VALUES ($1, $2, $3) RETURNING tutor_id, tutor_name, tutor_pic_url, tutor_profile", new_tutor.tutor_name, new_tutor.tutor_pic_url, new_tutor.tutor_profile).fetch_one(pool).await?;
    Ok(Tutor {
        tutor_id: tutor_row.tutor_id,
        tutor_name: tutor_row.tutor_name,
        tutor_pic_url: tutor_row.tutor_pic_url,
        tutor_profile: tutor_row.tutor_profile,
    })
}

pub async fn delete_tutor_db(pool: &PgPool, tutor_id: i32) -> Result<String, AppError> {
    let tutor_row = sqlx::query!("DELETE FROM ezy_tutor WHERE tutor_id = $1", tutor_id)
        .execute(pool)
        .await
        .map_err(|_err| AppError::DBError("Unable to delete tutor ".into()))?;
    Ok(format!("Delete {:#?} record", tutor_row))
}
